package eva

import (
	"testing"

	"gitcode.com/deyiyangyang/gascheme/lex"
	"gitcode.com/deyiyangyang/gascheme/obj"
	"gitcode.com/deyiyangyang/gascheme/par"
)

// TestCondWithTrueClause tests cond expressions where the first clause is true
func TestCondWithTrueClause(t *testing.T) {
	// Create a cond expression: (cond (#t 1) (#f 2))
	input := "(cond (#t 1) (#f 2))"

	// Parse the expression
	l := lex.NewLexer(input)
	p := par.NewParser(l)
	expr, err := p.ParseExpression()
	if err != nil {
		t.Fatalf("Parse error: %s", err)
	}

	// Create environment and register built-in procedures
	env := obj.NewGlobalEnvironment()
	RegisterBuiltinProcedures(env)

	// Evaluate the expression
	result := Eval(expr, env)

	// Check if the result is an Integer object with value 1
	integer, ok := result.(*obj.Integer)
	if !ok {
		t.Fatalf("Eval didn't return Integer. Got %T", result)
	}

	if integer.Value != 1 {
		t.Errorf("Eval returned wrong value. Got %d, want 1", integer.Value)
	}
}

// TestCondWithFalseThenTrueClause tests cond expressions where the first clause is false and second is true
func TestCondWithFalseThenTrueClause(t *testing.T) {
	// Create a cond expression: (cond (#f 1) (#t 2))
	input := "(cond (#f 1) (#t 2))"

	// Parse the expression
	l := lex.NewLexer(input)
	p := par.NewParser(l)
	expr, err := p.ParseExpression()
	if err != nil {
		t.Fatalf("Parse error: %s", err)
	}

	// Create environment and register built-in procedures
	env := obj.NewGlobalEnvironment()
	RegisterBuiltinProcedures(env)

	// Evaluate the expression
	result := Eval(expr, env)

	// Check if the result is an Integer object with value 2
	integer, ok := result.(*obj.Integer)
	if !ok {
		t.Fatalf("Eval didn't return Integer. Got %T", result)
	}

	if integer.Value != 2 {
		t.Errorf("Eval returned wrong value. Got %d, want 2", integer.Value)
	}
}

// TestCondWithElseClause tests cond expressions with an else clause
func TestCondWithElseClause(t *testing.T) {
	// Create a cond expression: (cond (#f 1) (else 2))
	input := "(cond (#f 1) (else 2))"

	// Parse the expression
	l := lex.NewLexer(input)
	p := par.NewParser(l)
	expr, err := p.ParseExpression()
	if err != nil {
		t.Fatalf("Parse error: %s", err)
	}

	// Create environment and register built-in procedures
	env := obj.NewGlobalEnvironment()
	RegisterBuiltinProcedures(env)

	// Evaluate the expression
	result := Eval(expr, env)

	// Check if the result is an Integer object with value 2
	integer, ok := result.(*obj.Integer)
	if !ok {
		t.Fatalf("Eval didn't return Integer. Got %T", result)
	}

	if integer.Value != 2 {
		t.Errorf("Eval returned wrong value. Got %d, want 2", integer.Value)
	}
}

// TestCondWithNoMatchingClause tests cond expressions with no matching clause and no else clause
func TestCondWithNoMatchingClause(t *testing.T) {
	// Create a cond expression: (cond (#f 1) (#f 2))
	input := "(cond (#f 1) (#f 2))"

	// Parse the expression
	l := lex.NewLexer(input)
	p := par.NewParser(l)
	expr, err := p.ParseExpression()
	if err != nil {
		t.Fatalf("Parse error: %s", err)
	}

	// Create environment and register built-in procedures
	env := obj.NewGlobalEnvironment()
	RegisterBuiltinProcedures(env)

	// Evaluate the expression
	result := Eval(expr, env)

	// Check if the result is nil
	if result != nil {
		t.Errorf("Eval returned wrong value. Got %v, want nil", result)
	}
}

// TestCondWithMultipleExpressionsInClause tests cond expressions with multiple expressions in a clause
func TestCondWithMultipleExpressionsInClause(t *testing.T) {
	// Create a cond expression: (cond (#f 1) (#t (define x 5) (+ x 1)))
	input := "(cond (#f 1) (#t (define x 5) (+ x 1)))"

	// Parse the expression
	l := lex.NewLexer(input)
	p := par.NewParser(l)
	expr, err := p.ParseExpression()
	if err != nil {
		t.Fatalf("Parse error: %s", err)
	}

	// Create environment and register built-in procedures
	env := obj.NewGlobalEnvironment()
	RegisterBuiltinProcedures(env)

	// Evaluate the expression
	result := Eval(expr, env)

	// Check if the result is an Integer object with value 6
	integer, ok := result.(*obj.Integer)
	if !ok {
		t.Fatalf("Eval didn't return Integer. Got %T", result)
	}

	if integer.Value != 6 {
		t.Errorf("Eval returned wrong value. Got %d, want 6", integer.Value)
	}
}

// TestCondWithComplexExpressions tests cond expressions with complex test expressions
func TestCondWithComplexExpressions(t *testing.T) {
	// Create a cond expression: (cond ((< 5 3) 1) ((> 5 3) 2) (else 3))
	input := "(cond ((< 5 3) 1) ((> 5 3) 2) (else 3))"

	// Parse the expression
	l := lex.NewLexer(input)
	p := par.NewParser(l)
	expr, err := p.ParseExpression()
	if err != nil {
		t.Fatalf("Parse error: %s", err)
	}

	// Create environment and register built-in procedures
	env := obj.NewGlobalEnvironment()
	RegisterBuiltinProcedures(env)

	// Evaluate the expression
	result := Eval(expr, env)

	// Check if the result is an Integer object with value 2
	integer, ok := result.(*obj.Integer)
	if !ok {
		t.Fatalf("Eval didn't return Integer. Got %T", result)
	}

	if integer.Value != 2 {
		t.Errorf("Eval returned wrong value. Got %d, want 2", integer.Value)
	}
}

// TestCondWithSingleExpressionInClause tests cond expressions with a single expression in a clause
func TestCondWithSingleExpressionInClause(t *testing.T) {
	// Create a cond expression: (cond (#f 1) (#t 42))
	input := "(cond (#f 1) (#t 42))"

	// Parse the expression
	l := lex.NewLexer(input)
	p := par.NewParser(l)
	expr, err := p.ParseExpression()
	if err != nil {
		t.Fatalf("Parse error: %s", err)
	}

	// Create environment and register built-in procedures
	env := obj.NewGlobalEnvironment()
	RegisterBuiltinProcedures(env)

	// Evaluate the expression
	result := Eval(expr, env)

	// Check if the result is an Integer object with value 42
	integer, ok := result.(*obj.Integer)
	if !ok {
		t.Fatalf("Eval didn't return Integer. Got %T", result)
	}

	if integer.Value != 42 {
		t.Errorf("Eval returned wrong value. Got %d, want 42", integer.Value)
	}
}